package cn.lg.soar.common.util;

import cn.lg.soar.common.util.data.StringUtil;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;

/**
 * 时间工具
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public interface DatetimeUtil {

    /**
     * 一天的毫秒数
     */
    long day_seconds = 24 * 3600;

    /**
     * 一天的毫秒数
     */
    long day_ms = day_seconds * 1000;

    /**
     * 一年的天数
     */
    float year_days = 365.25f;

    /**
     * 一年的毫秒数
     */
    long year_seconds = 31557600;

    /**
     * 一年的毫秒数
     */
    long year_ms = year_seconds * 1000;

    /**
     * 年月日格式
     */
    String PATTERN = "yyyy-MM-dd HH:mm:ss";
    ZoneId zoneId = ZoneId.systemDefault();
    DateTimeFormatter FORMAT_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    DateTimeFormatter FORMAT_TIME = DateTimeFormatter.ofPattern("HH:mm:ss");
    DateTimeFormatter FORMAT_DATETIME = DateTimeFormatter.ofPattern(PATTERN);
    DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    DateTimeFormatter yyyyMMddHHmm = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
    DateTimeFormatter yyyyMMddHH = DateTimeFormatter.ofPattern("yyyyMMddHH");
    DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");

    DateTimeFormatter HHmmssSSS = DateTimeFormatter.ofPattern("HHmmssSSS");
    DateTimeFormatter HHmmss = DateTimeFormatter.ofPattern("HHmmss");
    DateTimeFormatter HHmm = DateTimeFormatter.ofPattern("HHmm");
    DateTimeFormatter HH = DateTimeFormatter.ofPattern("HH");

    /**
     * String 转 LocalDateTime
     * @param date
     * @return
     */
    static LocalDateTime from(String date) {
        date = StringUtil.getNumber(date);
        switch (date.length()) {
            case 17:
                return LocalDateTime.of(
                        LocalDate.parse(date.substring(0, 8), yyyyMMdd),
                        LocalTime.parse(date.substring(8), HHmmssSSS)
                );
            case 14: return LocalDateTime.parse(date, yyyyMMddHHmmss);
            case 12: return LocalDateTime.parse(date, yyyyMMddHHmm);
            case 10: return LocalDateTime.parse(date, yyyyMMddHH);
            case 8: return LocalDateTime.of(LocalDate.parse(date, yyyyMMdd), LocalTime.MIN);
            case 6: return LocalDateTime.of(LocalDate.parse(date + "01", yyyyMMdd), LocalTime.MIN);
            case 4: return LocalDateTime.of(LocalDate.parse(date + "0101", yyyyMMdd), LocalTime.MIN);
            default: return from(Long.parseLong(date));
        }
    }

    /**
     * String 转 LocalDateTime
     * @param date
     * @param format
     * @return
     */
    static LocalDateTime from(String date, DateTimeFormatter formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalDateTime.parse(date, formatter);
        } catch (RuntimeException e) {
            return from(date);
        }
    }

    /**
     * String 转 LocalDateTime
     * @param date
     * @param format
     * @return
     */
    static LocalDateTime from(String date, String formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalDateTime.parse(date, DateTimeFormatter.ofPattern(formatter));
        } catch (RuntimeException e) {
            return from(date);
        }
    }

    /**
     * Date 转 LocalDateTime
     * @param date
     * @return
     */
    static LocalDateTime from(Date date) {
        return date.toInstant().atZone( zoneId ).toLocalDateTime();
    }

    /**
     * 毫秒值 转 LocalDateTime
     * @param millisecond
     * @return
     */
    static LocalDateTime from(long millisecond) {
        Instant instant = Instant.ofEpochMilli(millisecond);
        ZoneId zone = zoneId;
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 字符串转日期
     * @param date
     * @return
     */
    static LocalDate toLocalDate(String date) {
        date = StringUtil.getNumber(date);
        int length = date.length();
        switch (length) {
            case 8: return LocalDate.parse(date, yyyyMMdd);
            case 6: return LocalDate.parse(date + "01", yyyyMMdd);
            case 4: return LocalDate.parse(date + "0101", yyyyMMdd);
            default:
                if (length > 8) {
                    return LocalDate.parse(date.substring(0, 8), yyyyMMdd);
                }
                throw new RuntimeException("Unknown format");
        }
    }

    /**
     * 字符串转日期
     * @param date
     * @param formatter
     * @return
     */
    static LocalDate toLocalDate(String date, DateTimeFormatter formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalDate.parse(date, formatter);
        } catch (RuntimeException e) {
            return toLocalDate(date);
        }
    }

    /**
     * 字符串转日期
     * @param date
     * @param formatter
     * @return
     */
    static LocalDate toLocalDate(String date, String formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalDate.parse(date, DateTimeFormatter.ofPattern(formatter));
        } catch (RuntimeException e) {
            return toLocalDate(date);
        }
    }

    /**
     * 字符串转时间
     * @param date
     * @return
     */
    static LocalTime toLocalTime(String date) {
        date = StringUtil.getNumber(date);
        switch (date.length()) {
            case 17: return LocalTime.parse(date.substring(8), HHmmssSSS);
            case 14: return LocalTime.parse(date.substring(8), HHmmss);
            case 9: return LocalTime.parse(date, HHmmssSSS);
            case 6: return LocalTime.parse(date, HHmmss);
            case 4: return LocalTime.parse(date, HHmm);
            case 2: return LocalTime.parse(date, HH);
            default: throw new RuntimeException("Unknown format");
        }
    }

    /**
     * 字符串转时间
     * @param date
     * @param formatter
     * @return
     */
    static LocalTime toLocalTime(String date, DateTimeFormatter formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalTime.parse(date, formatter);
        } catch (RuntimeException e) {
            return toLocalTime(date);
        }
    }

    /**
     * 字符串转日期
     * @param date
     * @param formatter
     * @return
     */
    static LocalTime toLocalTime(String date, String formatter) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return LocalTime.parse(date, DateTimeFormatter.ofPattern(formatter));
        } catch (RuntimeException e) {
            return toLocalTime(date);
        }
    }

    /**
     * 转成 Date
     * @param localDateTime
     *
     * @return
     */
    static Date toDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone( zoneId ).toInstant());
    }

    /**
     * 转成 Date
     * @param localDate
     * @param localTime
     * @return
     */
    static Date toDate(LocalDate localDate, LocalTime localTime) {
        return toDate(localDate.atTime(localTime));
    }

    /**
     * 转成 Date
     * @param localDate
     * @return
     */
    static Date toDate(LocalDate localDate) {
        return toDate(localDate.atTime(LocalTime.now()));
    }

    /**
     * 转成 Date
     * @param localTime
     * @return
     */
    static Date toDate(LocalTime localTime) {
        return toDate(localTime.atDate(LocalDate.now()));
    }

    /**
     * long转date
     * @param time 毫秒值
     * @return
     */
    static Date toDate(Long time) {
        if (time == null) {
            return null;
        }
        return new Date(time);
    }

    /**
     * 字符串转date
     * @param date 时间日期
     * @return
     */
    static Date toDate(String date) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return strToDate(date);
        } catch (ParseException e) {
            throw new RuntimeException("解析时间失败", e);
        }
    }

    /**
     * 字符串转date
     * @param date 时间日期
     * @param format 格式
     * @return
     */
    static Date toDate(String date, String format) {
        if (StringUtil.isBlank(date)) {
            return null;
        }
        try {
            return new SimpleDateFormat(format).parse(date);
        } catch (ParseException e) {
            try {
                return strToDate(date);
            } catch (ParseException parseException) {
                throw new RuntimeException("解析时间失败", e);
            }
        }
    }

    /**
     * 字符串转日期
     * @param date 时间日期
     * @return
     * @throws ParseException
     */
    static Date strToDate(String date) throws ParseException {
        date = StringUtil.getNumber(date);
        switch (date.length()) {
            case 4: return new SimpleDateFormat("yyyy").parse(date);
            case 6: return new SimpleDateFormat("yyyyMM").parse(date);
            case 8: return new SimpleDateFormat("yyyyMMdd").parse(date);
            case 10: return new SimpleDateFormat("yyyyMMddHH").parse(date);
            case 12: return new SimpleDateFormat("yyyyMMddHHmm").parse(date);
            case 14: return new SimpleDateFormat("yyyyMMddHHmmss").parse(date);
            case 17: return new SimpleDateFormat("yyyyMMddHHmmssSSS").parse(date);
            default: return new Date(Long.parseLong(date));
        }
    }

    /**
     * LocalDateTime 转成 字符串
     * @param localDateTime
     * @return
     */
    static String format(LocalDateTime localDateTime, DateTimeFormatter formatter) {
        return localDateTime.format(formatter);
    }
    static String format(LocalDateTime localDateTime) {
        return format(localDateTime, FORMAT_DATETIME);
    }

    /**
     * LocalDate 转成 字符串
     * @param localDate
     * @return
     */
    static String format(LocalDate localDate, DateTimeFormatter formatter) {
        return localDate.format(formatter);
    }
    static String format(LocalDate localDate) {
        return format(localDate, FORMAT_DATE);
    }

    /**
     * LocalTime 转成 字符串
     * @param localTime
     * @return
     */
    static String format(LocalTime localTime, DateTimeFormatter formatter) {
        return localTime.format(formatter);
    }
    static String format(LocalTime localTime) {
        return format(localTime, FORMAT_TIME);
    }

    /**
     * Date 转成 字符串
     * @param date
     * @return
     */
    static String format(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }
    static String format(Date date) {
        return format(date, PATTERN);
    }

    /**
     * 获取某个时间的开始时间点
     * @param value
     * @param unit
     * @return
     */
    static LocalDateTime first(LocalDateTime value, ChronoUnit unit) {
        LocalDate date = value.toLocalDate();
        LocalTime time = value.toLocalTime();
        int[] dateValue = new int[]{ date.getYear(), date.getMonthValue(), date.getDayOfMonth() };
        int[] timeValue = new int[]{ time.getHour(), time.getMinute(), time.getSecond(), time.getNano() };
        switch (unit) {
            case MILLENNIA: dateValue[ 0 ] = dateValue[ 0 ] / 1000 * 1000;
            case CENTURIES: dateValue[ 0 ] = dateValue[ 0 ] / 100 * 100;
            case DECADES: dateValue[ 0 ] = dateValue[ 0 ] / 10 * 10;
            case YEARS: dateValue[ 1 ] = 1;
            case MONTHS: dateValue[ 2 ] = 1;
            case DAYS: timeValue[ 0 ] = 0;
            case HOURS: timeValue[ 1 ] = 0;
            case MINUTES: timeValue[ 2 ] = 0;
            case SECONDS: timeValue[ 3 ] = 0; break;
            case WEEKS:
                date = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
                return LocalDateTime.of(date, LocalTime.MIN);
            default: throw new RuntimeException("Does not support units");
        }
        return LocalDateTime.of(
                dateValue[ 0 ], dateValue[ 1 ], dateValue[ 2 ],
                timeValue[0], timeValue[1], timeValue[2], timeValue[3]
        );
    }

    /**
     * 获取某个时间的开始时间点
     * @param unit
     * @return
     */
    static LocalDateTime first(ChronoUnit unit) {
        return first(LocalDateTime.now(), unit);
    }

    /**
     * 获取某个时间的结束时间点
     * @param value
     * @param unit
     * @return
     */
    static LocalDateTime last(LocalDateTime value, ChronoUnit unit) {
        LocalDate date = value.toLocalDate();
        LocalTime time = value.toLocalTime();
        int[] dateValue = new int[]{ date.getYear(), date.getMonthValue(), date.getDayOfMonth() };
        int[] timeValue = new int[]{ time.getHour(), time.getMinute(), time.getSecond(), time.getNano() };
        switch (unit) {
            case MILLENNIA: dateValue[ 0 ] = dateValue[ 0 ] / 1000 * 1000 + 900;
            case CENTURIES: dateValue[ 0 ] = dateValue[ 0 ] / 100 * 100 + 90;
            case DECADES: dateValue[ 0 ] = dateValue[ 0 ] / 10 * 10 + 9;
            case YEARS: dateValue[ 1 ] = 12;
            case MONTHS:
                date = LocalDate.of(dateValue[ 0 ], dateValue[ 1 ], dateValue[ 2 ]);
                date = date.with(TemporalAdjusters.lastDayOfMonth());
                dateValue[ 2 ] = date.getDayOfMonth();
            case DAYS: timeValue[ 0 ] = 23;
            case HOURS: timeValue[ 1 ] = 59;
            case MINUTES: timeValue[ 2 ] = 59;
            case SECONDS: timeValue[ 3 ] = 999999999;break;
            case WEEKS:
                date = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY ));
                return LocalDateTime.of(date, LocalTime.MAX);
            default: throw new RuntimeException("Does not support units");
        }
        return LocalDateTime.of(
                dateValue[ 0 ], dateValue[ 1 ], dateValue[ 2 ],
                timeValue[0], timeValue[1], timeValue[2], timeValue[3]
        );
    }

    /**
     * 获取某个时间的结束时间点
     * @param unit
     * @return
     */
    static LocalDateTime last(ChronoUnit unit) {
        return last(LocalDateTime.now(), unit);
    }

    /**
     * 时间差值格式
     * @param seconds 秒
     * @return
     */
    static String timeDifference(long seconds){
        if(seconds < 60){
            return seconds + "秒钟";
        }
        if(seconds < 3600){
            return (seconds/60) + "分钟";
        }
        if(seconds < 86400){
            return (seconds/3600) + "小时";
        }
        if(seconds < 2629800){
            return (seconds/86400) + "天";
        }
        if(seconds < 31557600){
            return (seconds/2629800) + "个月";
        }
        return (seconds/31557600) + "年";
    }

    /**
     * 时间差值格式
     * @param duration
     * @return
     */
    static String timeDifference(Duration duration) {
        return timeDifference(duration.getSeconds());
    }

    /**
     * 从指定时间直到现在
     * @param datetime 过去时间点
     * @return
     */
    static String untilNow(LocalDateTime datetime) {
        return timeDifference(datetime.until(LocalDateTime.now(), ChronoUnit.SECONDS));
    }

    /**
     * 从指定时间直到现在
     * @param date 过去时间点
     * @return
     */
    static String untilNow(Date date) {
        return timeDifference((System.currentTimeMillis() - date.getTime()) / 1000);
    }

    /**
     * 从现在直到指定时间
     * @param datetime 将来时间点
     * @return
     */
    static String untilFuture(LocalDateTime datetime) {
        return timeDifference(LocalDateTime.now().until(datetime, ChronoUnit.SECONDS));
    }

    /**
     * 从现在直到指定时间
     * @param date 将来时间点
     * @return
     */
    static String untilFuture(Date date) {
        return timeDifference((date.getTime() - System.currentTimeMillis()) / 1000);
    }

    /**
     * 时间戳（毫秒值）
     * @param localDateTime
     * @return
     */
    static long timestamp(LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(zoneId).toInstant();
        return instant.toEpochMilli();
    }

    /**
     * 获取当前时间戳（毫秒值）
     * @return
     */
    static long timestamp() {
        return timestamp(LocalDateTime.now());
    }

    /**
     * 时间戳(秒值)
     * @param localDateTime
     * @return
     */
    static long second(LocalDateTime localDateTime) {
        return timestamp(localDateTime) / 1000;
    }

    /**
     * 获取当前时间戳(秒值)
     * @return
     */
    static long second() {
        return second(LocalDateTime.now());
    }

    /**
     * 获取时间戳：yyyyMMddHHmmss
     * @return
     */
    static String ymdhms() {
        return LocalDateTime.now().format(yyyyMMddHHmmss);
    }

    /**
     * 获取时间戳：yyyyMMddSecond
     * @return
     */
    static String ymdSecond() {
        LocalDateTime now = LocalDateTime.now();
        LocalDate date = now.toLocalDate();
        LocalTime time = now.toLocalTime();
        return date.format(yyyyMMdd) + time.toSecondOfDay();
    }

    /**
     * 计算年龄
     * @param birthday 生日
     * @return
     */
    static double calcAge(long birthday) {
        return (System.currentTimeMillis() - birthday) / (1000 * 3600 * 24 * 365.25);
    }

    /**
     * 计算年龄
     * @param birthday
     * @return
     */
    static Double calcAge(Date birthday) {
        if (birthday == null) {
            return null;
        }
        return calcAge(birthday.getTime());
    }

    /**
     * 计算年龄
     * @param birthday
     * @return
     */
    static Double calcAge(LocalDateTime birthday) {
        if (birthday == null) {
            return null;
        }
        return calcAge(timestamp(birthday));
    }

    /**
     * 计算年龄
     * @param birthday
     * @return
     */
    static Double calcAge(LocalDate birthday) {
        if (birthday == null) {
            return null;
        }
        return calcAge(LocalDateTime.of(birthday, LocalTime.MIN));
    }

    /**
     * 计算生日
     * @param age 年龄
     * @return
     */
    static LocalDateTime calcBirthday(Double age) {
        if (age == null) {
            return null;
        }
        double birthday = System.currentTimeMillis() - (1000 * 3600 * 24 * 365.25 * age);
        return from((long) birthday);
    }
}
